forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1import { CACHE_MAX_AGE_ONE_DAY } from '#shared/utils/constants'
2
3type GitHubContributorWeek = {
4 w: number
5 a: number
6 d: number
7 c: number
8}
9
10type GitHubContributorStats = {
11 total: number
12 weeks: GitHubContributorWeek[]
13}
14
15export default defineCachedEventHandler(
16 async event => {
17 const owner = getRouterParam(event, 'owner')
18 const repo = getRouterParam(event, 'repo')
19
20 if (!owner || !repo) {
21 throw createError({
22 status: 400,
23 message: 'repository not provided',
24 })
25 }
26
27 const url = `https://api.github.com/repos/${owner}/${repo}/stats/contributors`
28 const headers = {
29 'User-Agent': 'npmx',
30 'Accept': 'application/vnd.github+json',
31 }
32
33 const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
34 const maxAttempts = 6
35 let delayMs = 1000
36
37 try {
38 for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
39 const response = await $fetch.raw<GitHubContributorStats[]>(url, { headers })
40 const status = response.status
41
42 if (status === 200) {
43 return Array.isArray(response._data) ? response._data : []
44 }
45
46 if (status === 204) {
47 return []
48 }
49
50 if (status === 202) {
51 if (attempt === maxAttempts - 1) return []
52 await sleep(delayMs)
53 delayMs = Math.min(delayMs * 2, 16_000)
54 continue
55 }
56
57 return []
58 }
59
60 return []
61 } catch {
62 return []
63 }
64 },
65 {
66 maxAge: CACHE_MAX_AGE_ONE_DAY,
67 },
68)